﻿using Msdn5.DataCipher.ThirdParty;
using Msdn5.Framework;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace Msdn5.DataCipher
{
    /// <summary>
    /// 使用BouncyCastle的加解密
    /// </summary>
    public class Msdn5DataCipher
    {
        public Msdn5DataCipher()
        {

        }
        /// <summary>
        /// 摘要_哈希算法
        /// </summary>
        /// <param name="option">加密选项</param>
        /// <param name="model">模式</param>
        /// <returns>结果</returns>
        public CipherResult GetDigestResult(CipherOption option, Msdn5CipherModel model)
        {
            CipherResult result = new CipherResult();
            try
            {
                if (model.CipherType == Msdn5CipherTypeEnum.摘要_哈希算法)
                {
                    if (option.SaltKeyBytes.Length > 0)
                    {
                        option.SourceBytes = option.SourceBytes.Concat(option.SaltKeyBytes).ToArray();
                    }
                    var digest = DigestUtilities.GetDigest(model.Identifier);
                    digest.BlockUpdate(option.SourceBytes, 0, option.SourceBytes.Length);
                    result.ResultBytes = new byte[digest.GetDigestSize()];
                    digest.DoFinal(result.ResultBytes, 0);
                }
                else
                {
                    result.CipherException = new Exception("请使用摘要算法计算数据!");
                }

            }
            catch (Exception ex)
            {

                result.CipherException = ex;
            }
            return result;
        }
        /// <summary>
        /// 非对称加解密
        /// </summary>
        /// <param name="option">加密选项</param>
        /// <param name="model">模式</param>
        /// <returns>结果</returns> 
        public CipherResult GetAsymmetricResult(CipherOption option, Msdn5CipherModel model)
        {
            CipherResult result = new CipherResult();
            try
            {

                if (model.Identifier == null)
                {
                    model.Identifier = CipherUtilities.GetObjectIdentifier(model.Id);
                }

                IBufferedCipher cipher = model.Identifier == null ? CipherUtilities.GetCipher(model.DisplayContext) : CipherUtilities.GetCipher(model.Identifier);
                RsaKeyParameters parameters = null;
                if (option.KeyBytes.Length > 0)
                {
                    parameters = (RsaKeyParameters)PublicKeyFactory.CreateKey(option.KeyBytes);
                }
                else if (option.IVBytes.Length > 0)
                {
                    parameters = (RsaKeyParameters)PrivateKeyFactory.CreateKey(option.KeyBytes);
                }
                else
                {
                    result.CipherException = new Exception("请设置RSA公钥或私钥!");
                    return result;
                }
                cipher.Init(option.Encrypted, parameters);

            }
            catch (Exception ex)
            {
                result.CipherException = ex;
            }

            return result;
        }
        /// <summary>
        /// 对称加解密
        /// </summary>
        /// <param name="option">加密选项</param>
        /// <param name="model">模式</param>
        /// <returns>结果</returns> 
        public CipherResult GetCipherResult(CipherOption option, Msdn5CipherModel model)
        {
            CipherResult result = new CipherResult();
            try
            {
                if (model.DisplayContext.StartsWith("SM4"))
                {
                    var info = model.DisplayContext.SplitEx("/");
                    var mode = info[1];
                    var pad = info[2];
                    Sm4Context ctx = new Sm4Context
                    {
                        IsPadding = pad.EqualsIgnoreCase("Padding") ? true : false,
                    };
                    SM4 sm4 = new SM4();
                    //检查Key
                    if (option.KeyBytes.Length != 16)
                    {
                        throw new ArgumentOutOfRangeException("秘钥(Key)的长度必须为16个字节即128bit");
                    }
                    if (option.Encrypted)
                    {
                        sm4.SetKeyEnc(ctx, option.KeyBytes);
                    }
                    else
                    {
                        sm4.SetKeyDec(ctx, option.KeyBytes);
                    }
                    if (mode.EqualsIgnoreCase("ECB"))
                    {
                        result.ResultBytes = sm4.Sm4CryptEcb(ctx, option.SourceBytes);
                    }
                    else
                    {
                        //检查IV
                        if (option.IVBytes.Length != 16)
                        {
                            throw new ArgumentOutOfRangeException("向量(IV)的长度必须为16个字节即128bit");
                        }
                        result.ResultBytes = sm4.Sm4CryptCbc(ctx, option.IVBytes, option.SourceBytes);
                    }



                }
                else
                {
                    if (model.Identifier == null)
                    {
                        model.Identifier = CipherUtilities.GetObjectIdentifier(model.Id);
                    }

                    IBufferedCipher cipher = model.Identifier == null ? CipherUtilities.GetCipher(model.DisplayContext) : CipherUtilities.GetCipher(model.Identifier);
                    ICipherParameters parameters = null;
                    if (model.Identifier == null)
                    {
                        string alg = cipher.AlgorithmName;
                        int index = cipher.AlgorithmName.IndexOf("/");
                        if (index > -1)
                        {
                            alg = cipher.AlgorithmName.Substring(0, index);
                        }
                        try
                        {
                            parameters = ParameterUtilities.CreateKeyParameter(alg, option.KeyBytes);
                        }
                        catch
                        {
                            parameters = null;
                        }
                    }
                    else
                    {
                        try
                        {
                            parameters = ParameterUtilities.CreateKeyParameter(model.Identifier, option.KeyBytes);
                        }
                        catch (Exception ex)
                        {

                            if (ex is SecurityUtilityException) //不支持创建Key
                            {
                                parameters = null;
                            }
                            else
                            {
                                result.CipherException = ex;
                                return result;
                            }
                        }

                    }

                    try
                    {
                        if (parameters != null)
                        {
                            var noIvArray = new string[] {
                            "Gost28147",
                            "RC4",
                            "RC5-32",
                            "DESede",

                        };
                            if (model.DisplayContext.IndexOf("/ECB") < 0 && !noIvArray.Any(x => cipher.AlgorithmName.EqualsIgnoreCase(x)))
                            {
                                parameters = new ParametersWithIV(parameters, option.IVBytes);
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        if (ex is SecurityUtilityException) //不支持创建IV
                        {
                            parameters = null;
                        }
                        else if (ex is ArgumentOutOfRangeException)
                        {
                            throw;
                        }
                    }
                    if (parameters == null && model.DisplayContext.StartsWithIgnoreCase("PBE"))
                    {
                        //PEB参数

                        //option.SaltKeyBytes = Hex.Decode("b07bf522c8d608b8");
                        //option.PEBPassWord = "123456".ToArray();
                        //option.PEBIterationCount = 100;
                        Asn1Encodable algParams = PbeUtilities.GenerateAlgorithmParameters(model.Identifier, option.SaltKeyBytes, option.PEBIterationCount);
                        parameters = PbeUtilities.GenerateCipherParameters(
                            model.Identifier, option.PEBPassWord.ToArray(), algParams);


                    }
                    cipher.Init(option.Encrypted, parameters);

                    result.ResultBytes = cipher.DoFinal(option.SourceBytes);
                }
            }
            catch (Exception ex)
            {
                result.CipherException = ex;
            }
            return result;
        }

    }

    public class Msdn5CipherModel
    {
        public Msdn5CipherTypeEnum CipherType { get; set; } = Msdn5CipherTypeEnum.对称加解密;
        public bool Visibility { get; set; } = true;
        public DerObjectIdentifier Identifier { get; set; }

        public string Id { get; set; }

        public string DisplayContext { get; set; }

        public string BitFlag { get; set; }

        public void Filter(string filter)
        {
            Visibility = DisplayContext.IndexOfIgnoreCase(filter);
        }

    }

    public enum Msdn5CipherTypeEnum
    {

        摘要_哈希算法,
        非对称加解密,
        对称加解密
    }

    
    /// <summary>
    /// 加密时使用的参数
    /// </summary>
    public class CipherOption
    {
        /// <summary>
        /// 原文字节数组
        /// </summary>
        public byte[] SourceBytes { get; set; } = null;
        /// <summary>
        /// 加盐密钥字节数组
        /// </summary>
        public byte[] SaltKeyBytes { get; set; } = new byte[0];
        /// <summary>
        /// PEB的密码
        /// </summary>
        public string PEBPassWord { get; set; } = "";
        /// <summary>
        /// PEB的迭代次数
        /// </summary>
        public int PEBIterationCount { get; set; }
        /// <summary>
        /// 对称加密的密钥KEY
        /// (默认为长度为0的数组)
        /// </summary>
        public byte[] KeyBytes { get; set; } = new byte[0];

        /// <summary>
        /// 对称加密的向量IV
        /// (默认为长度为0的数组)
        /// </summary>
        public byte[] IVBytes { get; set; } = new byte[0];

        /// <summary>
        /// 加密为True
        /// 解密为FALSE
        /// </summary>
        public bool Encrypted { get; set; } = true;

        

    }


    /// <summary>
    /// 加解密结果类
    /// </summary>
    public class CipherResult
    {
        /// <summary>
        /// 原始结果的字节数组形式,默认为NULL
        /// </summary>
        public byte[] ResultBytes { get; set; } = null;

        /// <summary>
        /// 执行异常,默认为NULL,异常时有值
        /// </summary>
        public Exception CipherException { get; set; } = null;
    }


}
